﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FirstFloor.ModernUI.Presentation;
using FirstFloor.ModernUI.Windows.Controls;
using System.Windows.Media.Animation;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for ModulerMenu.xaml
    /// </summary>
    public partial class ModulerMenu : UserControl
    {
        /// <summary>
        /// Defines the SelectedSource dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedSourceProperty = DependencyProperty.Register("SelectedSource", typeof(Uri), typeof(ModulerMenu), new PropertyMetadata(OnSelectedSourceChanged));

        /// <summary>
        /// Defines the LinkGroups dependency property.
        /// </summary>
        public static readonly DependencyProperty LinkGroupsProperty = DependencyProperty.Register("LinkGroups", typeof(LinkGroupCollection), typeof(ModulerMenu), new PropertyMetadata(OnLinkGroupsChanged));

        /// <summary>
        /// Defines the SelectedLinkGroup dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedLinkGroupProperty = DependencyProperty.Register("SelectedLinkGroup", typeof(LinkGroup), typeof(ModulerMenu), new PropertyMetadata(OnSelectedLinkGroupChanged));

        /// <summary>
        /// Defines the SelectedLink dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedLinkProperty = DependencyProperty.Register("SelectedLink", typeof(Link), typeof(ModulerMenu), new PropertyMetadata(OnSelectedLinkChanged));

        private Dictionary<string, ReadOnlyLinkGroupCollection> groupMap = new Dictionary<string, ReadOnlyLinkGroupCollection>();     // stores LinkGroupCollections by GroupName
        private bool isSelecting;

        /// <summary>
        /// Occurs when the selected source has changed.
        /// </summary>
        public event EventHandler<SourceEventArgs> SelectedSourceChanged;

        private static void OnSelectedLinkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // update selected source
            var newValue = (Link)e.NewValue;
            Uri selectedSource = null;
            if (newValue != null)
            {
                selectedSource = newValue.Source;
            }
            ((ModulerMenu)d).SetCurrentValue(SelectedSourceProperty, selectedSource);
        }


        public Uri SelectedSource
        {
            get { return (Uri)GetValue(SelectedSourceProperty); }
            set { SetValue(SelectedSourceProperty, value); }
        }

        public LinkGroupCollection LinkGroups
        {
            get { return (LinkGroupCollection)GetValue(LinkGroupsProperty); }
            set { SetValue(LinkGroupsProperty, value); }
        }

        public LinkGroup SelectedLinkGroup
        {
            get { return (LinkGroup)GetValue(SelectedLinkGroupProperty); }
            set { SetValue(SelectedLinkGroupProperty, value); }
        }

        public Link SelectedLink
        {
            get { return (Link)GetValue(SelectedLinkProperty); }
            set { SetValue(SelectedLinkProperty, value); }
        }

        private static void OnSelectedSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ModulerMenu)d).OnSelectedSourceChanged((Uri)e.OldValue, (Uri)e.NewValue);
        }

        private void OnSelectedSourceChanged(Uri oldValue, Uri newValue)
        {
            if (!this.isSelecting)
            {
                // if old and new are equal, don't do anything
                if (newValue != null && newValue.Equals(oldValue))
                {
                    return;
                }

                UpdateSelection();
            }

            // raise SelectedSourceChanged event
            var handler = this.SelectedSourceChanged;
            if (handler != null)
            {
                handler(this, new SourceEventArgs(newValue));
            }
        }

        private static void OnLinkGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ModulerMenu)d).OnLinkGroupsChanged((LinkGroupCollection)e.OldValue, (LinkGroupCollection)e.NewValue);
        }

        private static void OnSelectedLinkGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // retrieve the selected link from the group
            var group = (LinkGroup)e.NewValue;
            Link selectedLink = null;
            if (group != null)
            {
                selectedLink = group.SelectedLink;

                // if no link selected or link doesn't exist in group links, auto-select first
                if (group.Links != null)
                {
                    if (selectedLink != null && !group.Links.Any(l => l == selectedLink))
                    {
                        selectedLink = null;
                    }

                    if (selectedLink == null)
                    {
                        selectedLink = group.Links.FirstOrDefault();
                    }
                }

                ModulerMenu that = d as ModulerMenu;
                if (!that.mainWrapper.Triggers.Contains(that.openMain))
                {
                    that.mainWrapper.Triggers.Add(that.openMain);
                    that.mainWrapper.Triggers.Add(that.closeMain);
                }

            }

            // update the selected link
            ((ModulerMenu)d).SetCurrentValue(SelectedLinkProperty, selectedLink);
        }

        /// <summary>
        /// Gets a non-null name for given group.
        /// </summary>
        /// <param name="group"></param>
        /// <returns></returns>
        private static string GetGroupName(LinkGroup group)
        {
            // use special key for GroupName <null>
            return group.GroupName ?? "<null>";
        }
        private readonly EventTrigger closeMain;
        private readonly EventTrigger openMain;
        public ModulerMenu()
        {
            InitializeComponent();

            closeMain = (EventTrigger)FindResource("closeMain");
            openMain = (EventTrigger)FindResource("openMain");
        }

        private void OnLinkGroupsChanged(LinkGroupCollection oldValue, LinkGroupCollection newValue)
        {
            if (oldValue != null)
            {
                // detach old event handler
                oldValue.CollectionChanged -= OnLinkGroupsCollectionChanged;
            }

            if (newValue != null)
            {
                // ensures the menu is rebuild when changes in the LinkGroups occur
                newValue.CollectionChanged += OnLinkGroupsCollectionChanged;
            }

            RebuildMenu(newValue);
        }

        private void RebuildMenu(LinkGroupCollection groups)
        {
            this.groupMap.Clear();
            if (groups != null)
            {
                // fill the group map based on group name
                foreach (var group in groups)
                {
                    var groupName = GetGroupName(group);

                    ReadOnlyLinkGroupCollection groupCollection;
                    if (!this.groupMap.TryGetValue(groupName, out groupCollection))
                    {
                        // create a new collection for this group name
                        groupCollection = new ReadOnlyLinkGroupCollection(new LinkGroupCollection());
                        this.groupMap.Add(groupName, groupCollection);
                    }

                    // add the group
                    groupCollection.List.Add(group);
                }
            }

            // update current selection
            UpdateSelection();
        }

        private void OnLinkGroupsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            RebuildMenu((LinkGroupCollection)sender);
        }

        public void UpdateSelection()
        {
            LinkGroup selectedGroup = null;
            Link selectedLink = null;

            if (LinkGroups != null)
            {
                var linkInfo = (from g in this.LinkGroups
                                from l in g.Links
                                where l.Source == SelectedSource
                                select new
                                {
                                    Link = l,
                                    Group = g
                                }).FirstOrDefault();

                if (linkInfo != null)
                {
                    selectedGroup = linkInfo.Group;
                    selectedLink = linkInfo.Link;
                }
                else
                {
                    // could not find link and group based on selected source, fall back to selected link group
                    selectedGroup = this.SelectedLinkGroup;

                    // if selected group doesn't exist in available groups, select first group
                    if (!this.LinkGroups.Any(g => g == selectedGroup))
                    {
                        selectedGroup = this.LinkGroups.FirstOrDefault();
                    }
                }
            }

            ReadOnlyLinkGroupCollection groups = null;
            if (selectedGroup != null)
            {
                // ensure group itself maintains the selected link
                selectedGroup.SelectedLink = selectedLink;

                // find the collection this group belongs to
                var groupName = GetGroupName(selectedGroup);
                this.groupMap.TryGetValue(groupName, out groups);
            }

            this.isSelecting = true;
            // update selection
            //SetValue(VisibleLinkGroupsPropertyKey, groups);
            SetCurrentValue(SelectedLinkGroupProperty, selectedGroup);
            SetCurrentValue(SelectedLinkProperty, selectedLink);

            if (SelectedSource != null && mainWrapper.Triggers.Contains(openMain))
            {
                mainWrapper.Triggers.Remove(openMain);
                mainWrapper.Triggers.Remove(closeMain);
            }

            this.isSelecting = false;
        }
    }
}
